﻿using System;
using System.Collections.Generic;
using System.Globalization;


namespace DataService.Core.Common
{
    public static class DateTimeHelper
    {
        private static readonly Int64 _maxSeconds = (Int64)(DateTime.MaxValue - DateTime.MinValue).TotalSeconds;
        private static readonly Int64 _maxMilliseconds = (Int64)(DateTime.MaxValue - DateTime.MinValue).TotalMilliseconds;
        private static readonly DateTime _dt1970 = new DateTime(1970, 1, 1);
        private static readonly DateTimeOffset _dto1970 = new DateTimeOffset(new DateTime(1970, 1, 1));
        /// <summary>去掉时间日期秒后面部分，可指定毫秒ms、分m、小时h</summary>
        /// <param name="value">时间日期</param>
        /// <param name="format">格式字符串，默认s格式化到秒，ms格式化到毫秒</param>
        /// <returns></returns>
        public static DateTime Trim(this DateTime value, String format = "s")
        {
            return format switch
            {
#if NET7_0_OR_GREATER
            "us" => new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, value.Millisecond, value.Microsecond, value.Kind),
#endif
                "ms" => new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, value.Millisecond, value.Kind),
                "s" => new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, value.Kind),
                "m" => new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, 0, value.Kind),
                "h" => new DateTime(value.Year, value.Month, value.Day, value.Hour, 0, 0, value.Kind),
                _ => value,
            };
        }

        /// <summary>转为时间日期，转换失败时返回最小时间。支持字符串、整数（Unix秒）</summary>
        /// <param name="value">待转换对象</param>
        /// <param name="defaultValue">默认值。待转换对象无效时使用</param>
        /// <returns></returns>
        public static DateTimeOffset ToDateTimeOffset(this Object value, DateTimeOffset defaultValue)
        {
            if (value is DateTimeOffset num) return num;
            if (value == null || value == DBNull.Value) return defaultValue;

            // 特殊处理字符串，也是最常见的
            if (value is String str ||
                (value is IList<String> list && list.Count > 0 && (str = list[0]) != null))
            {
                str = str.Trim();
                if (str.IsNullOrEmpty()) return defaultValue;

                if (DateTimeOffset.TryParse(str, out var dt)) return dt;
                return str.Contains("-") && DateTimeOffset.TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt)
                    ? dt
                    : str.Contains("/") && DateTimeOffset.TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt)
                    ? dt
                    : DateTimeOffset.TryParseExact(str, "yyyyMMddHHmmss", null, DateTimeStyles.None, out dt)
                    ? dt
                    : DateTimeOffset.TryParseExact(str, "yyyyMMdd", null, DateTimeStyles.None, out dt) ? dt : defaultValue;
            }

            // 特殊处理整数，Unix秒，绝对时间差，不考虑UTC时间和本地时间。
            if (value is Int32 k)
            {
                return k >= _maxSeconds || k <= -_maxSeconds ? defaultValue : _dto1970.AddSeconds(k);
            }
            if (value is Int64 m)
            {
                return m >= _maxMilliseconds || m <= -_maxMilliseconds
                    ? defaultValue
                    : m > 100 * 365 * 24 * 3600L ? _dto1970.AddMilliseconds(m) : _dto1970.AddSeconds(m);
            }

            try
            {
                return Convert.ToDateTime(value);
            }
            catch
            {
                return defaultValue;
            }
        }
        /// <summary>转为时间日期，转换失败时返回最小时间。支持字符串、整数（Unix秒）</summary>
        /// <param name="value">待转换对象</param>
        /// <param name="defaultValue">默认值。待转换对象无效时使用</param>
        /// <returns></returns>
        public static DateTime ToDateTime(this Object value, DateTime defaultValue)
        {
            if (value is DateTime num) return num;
            if (value == null || value == DBNull.Value) return defaultValue;

            // 特殊处理字符串，也是最常见的
            if (value is String str ||
                (value is IList<String> list && list.Count > 0 && (str = list[0]) != null))
            {
                //str = ToDBC(str).Trim();
                str = str.Trim();
                if (str.IsNullOrEmpty()) return defaultValue;

                // 处理UTC
                var utc = false;
                if (str.EndsWithIgnoreCase(" UTC"))
                {
                    utc = true;
                    //C#8特有的新特性，【范围取值】，[0..4]意思是 取值范围 索引0到索引4（不含索引4）
                    //语法： {startIndex}..{endIndex} ：包含 startIndex ， 不包含 endIndex
                    //限制 ：0 <= startIndex <= endIndex <= arr.Length ，其他均会报错！ startIndex 等于
                    //str[2..4] 意思 从索引2 到索引4（不含索引4） 
                    //[0..^4];^是末尾运算符, ^4是末尾倒数第4位，即是 str.length-4
                    str = str[0..^4];
                }

                if (!DateTime.TryParse(str, out var dt) &&
                    !(str.Contains('-') && DateTime.TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt)) &&
                    !(str.Contains('/') && DateTime.TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt)) &&
                    !DateTime.TryParseExact(str, "yyyyMMddHHmmss", null, DateTimeStyles.None, out dt) &&
                    !DateTime.TryParseExact(str, "yyyyMMdd", null, DateTimeStyles.None, out dt) &&
                    !DateTime.TryParse(str, out dt))
                {
                    dt = defaultValue;
                }

                // 处理UTC
                if (utc) dt = new DateTime(dt.Ticks, DateTimeKind.Utc);

                return dt;
            }

            // 特殊处理整数，Unix秒，绝对时间差，不考虑UTC时间和本地时间。
            if (value is Int32 k)
            {
                return k >= _maxSeconds || k <= -_maxSeconds ? defaultValue : _dt1970.AddSeconds(k);
            }
            if (value is Int64 m)
            {
                return m >= _maxMilliseconds || m <= -_maxMilliseconds
                    ? defaultValue
                    : m > 100 * 365 * 24 * 3600L ? _dt1970.AddMilliseconds(m) : _dt1970.AddSeconds(m);
            }

            try
            {
                return Convert.ToDateTime(value);
            }
            catch
            {
                return defaultValue;
            }
        }
    }
}
